import { bind, Event, JsonValue, react, reactJson, reactJsonWithUndefined, SceneObjectKey, track, UniteChanged } from "earthsdk3";
import { _getLayerConfig, _getport, _saveLayerConfig, LayerType, _getFeatureProperty } from "./types";
import { ESRtsFeatureEditing, FeatureVisableJsonType } from "earthsdk3";
import { ES3DTileset } from "earthsdk3";
import { GroupProperty, JsonProperty } from "earthsdk3"

/**
 * ESRtsTileset2 类用于表示和操作实时3D Tileset图层。它继承自 ES3DTileset 类，并扩展了实时数据和高亮功能。
 * 
 * 核心功能包括：
 * - 获取和设置图层配置
 * - 获取和设置要素 ID 数组
 * - 高亮和移除高亮数据服务要素
 * - 拾取要素
 * - 刷新图层
 * - 获取服务的基础 URL、图层服务名称和 IP 地址
 * 
 * 使用示例：
 * 
 * 构造函数参数：
 * - id: SceneObjectKey，场景对象 ID，可选。
 * 
 * 特殊使用限制或潜在的副作用：
 * - 在设置 URL 后，需要调用 `refresh` 方法以加载 Tileset。
 * - 高亮和拾取功能依赖于底层的 ES3DTileset 实现。
 */
export class ESRtsTileset2 extends ES3DTileset {
    static override readonly createDefaultProps = () => ({
        ...ES3DTileset.createDefaultProps(),
        /**图层配置 */
        layerConfig: reactJsonWithUndefined<JsonValue[]>([]),
    });
    /**
     * 静态属性，用于注册类的类型信息。
     */
    static override readonly type = this.register('ESRtsTileset2', this, { chsName: 'CIMRTS实时3DTileset图层', tags: ['ESObjects', '_ES_Impl_Cesium', '_ES_Impl_UE'], description: "实时3DTileset图层" });

    /**
     * 内部的 ES3DTileset 实例。
     */
    private _es3DTileset = this.dv(new ES3DTileset());

    /**
     * 获取内部的 ES3DTileset 实例。
     * @returns 内部的 ES3DTileset 实例。
     */
    get es3DTileset() { return this._es3DTileset; }

    /**
     * 要素 ID 数组。
     */
    private _ids = this.dv(reactJson<string[]>([]));

    /**
     * 获取要素 ID 数组。
     * @returns 要素 ID 数组。
     */
    get ids() { return this._ids.value; }

    /**
     * 获取要素 ID 数组变化事件。
     * @returns 要素 ID 数组变化事件。
     */
    get idsChanged() { return this._ids.changed; }

    /**
     * 设置要素 ID 数组。
     * @param value - 要设置的要素 ID 数组。
     */
    set ids(value: string[]) { this._ids.value = value; }


    private _useEarthDSToken = this.dv(react<boolean>(false));
    get useEarthDSToken() { return this._useEarthDSToken.value; }
    get useEarthDSTokenChanged() { return this._useEarthDSToken.changed; }
    set useEarthDSToken(value: boolean) { this._useEarthDSToken.value = value; }

    private _earthdstoken = this.dv(react<string>(""));
    get earthdstoken() { return this._earthdstoken.value; }
    get earthdstokenChanged() { return this._earthdstoken.changed; }
    set earthdstoken(value: string) { this._earthdstoken.value = value; }

    // 要素拾取的要素id数组
    private _visJson = this.disposeVar(react<any>({}));
    get visJson() { return this._visJson.value; }
    set visJson(value: any) { this._visJson.value = value; }
    get visJsonChanged() { return this._visJson.changed; }

    override setFeatureVisable(featureName: string, json: FeatureVisableJsonType[]) {
        if (this.layerConfig?.length) {
            const item: any = {}
            item[json[0].value as string] = json[0].visable;
            this.visJson = {...this.visJson, ...item};
        } else {
            this.setFeatureVisableEvent.emit(featureName, (typeof json === 'string') ? JSON.parse(json) : json);
        }
    }

    /**
     * 获取要素属性。
     * @param featureId - 要素 ID。
     * @returns 要素属性。
     */
    async getFeatureProperty(featureId: string) {
        const properties = await _getFeatureProperty(this.baseUrl, this.tileServiceName, featureId, this.useEarthDSToken, this.earthdstoken);
        return properties;
    }

    /**
     * 获取图层配置。
     * @returns 图层配置。
     */
    async getLayerConfig() {
        const layerConfig = await _getLayerConfig(this.baseUrl, this.tileServiceName, this.useEarthDSToken, this.earthdstoken);
        if (!layerConfig || !Array.isArray(layerConfig)) {
            this.layerConfig = []
        } else {
            this.layerConfig = layerConfig as unknown as JsonValue[];
        }
        return this.layerConfig;
    }

    /**
     * 保存图层配置。
     * @returns 保存结果。
     */
    async saveLayerConfig() {
        const layerConfig = this.layerConfig as unknown as LayerType[];
        const res = await _saveLayerConfig(this.baseUrl, this.tileServiceName, this.useEarthDSToken, this.earthdstoken, layerConfig);
        return res;
    }

    /**
     * 内部 3D Tileset 高亮事件。
     */
    private _highlightInner3DtilesetEvent = this.dv(new Event<[sceneObject: ESRtsFeatureEditing]>());

    /**
     * 获取内部 3D Tileset 高亮事件。
     * @returns 内部 3D Tileset 高亮事件。
     */
    get highlightInner3DtilesetEvent() { return this._highlightInner3DtilesetEvent; }

    /**
     * 触发内部 3D Tileset 高亮事件。
     * @param sceneObject - 场景对象。
     */
    _highlightInner3Dtileset(sceneObject: ESRtsFeatureEditing) { this._highlightInner3DtilesetEvent.emit(sceneObject); }

    /**
     * 高亮数据服务要素。
     * @param sceneObject - 场景对象。
     */
    async highlightDSFeature(sceneObject: ESRtsFeatureEditing) {
        this._highlightInner3Dtileset(sceneObject);
    };

    /**
     * 内部 3D Tileset 移除高亮事件。
     */
    private _removeHighlightInner3DtilesetEvent = this.dv(new Event<[sceneObject: ESRtsFeatureEditing]>());

    /**
     * 获取内部 3D Tileset 移除高亮事件。
     * @returns 内部 3D Tileset 移除高亮事件。
     */
    get removeHighlightInner3DtilesetEvent() { return this._removeHighlightInner3DtilesetEvent; }

    /**
     * 触发内部 3D Tileset 移除高亮事件。
     * @param sceneObject - 场景对象。
     */
    _removeHighlightInner3Dtileset(sceneObject: ESRtsFeatureEditing) { this._removeHighlightInner3DtilesetEvent.emit(sceneObject); }

    /**
     * 移除数据服务要素的高亮。
     * @param sceneObject - 场景对象。
     */
    async removeHighlightDSFeature(sceneObject: ESRtsFeatureEditing) {
        this._removeHighlightInner3Dtileset(sceneObject);
    };

    /**
     * 拾取要素。
     * @param viewer - 查看器对象。
     * @param screenPosition - 屏幕位置。
     * @returns 拾取结果。
     */
    async pickFeature(viewer: any, screenPosition: [number, number]) {
        const result = await viewer.pick(screenPosition);
        // console.log("result", result);

        const sceneObject = result.sceneObject;
        if (!sceneObject) return;
        if ((sceneObject.id !== this.id) && (sceneObject.typeName != "ESRtsFeatureEditing")) return;
        // 全局ESDSTileset对象要素ID
        let id = sceneObject.typeName != "ESRtsFeatureEditing" ? true : false;
        // 第一次点击
        if (id) {
            this.ids = [...this.ids, result?.tilesetPickInfo?.id];
            return {
                featureId: result?.tilesetPickInfo?.id,
                pickInnerFields: result?.tilesetPickInfo
            }
        }
        // 第二次拾取 
        else {
            return {
                featureId: result?.sceneObject?.id,
                pickInnerFields: result?.tilesetPickInfo
            }
        }
    };

    /**
     * 数据地址端口号。
     */
    private _tilesetServePort = this.dv(react<string>(""));

    /**
     * 获取数据地址端口号。
     * @returns 数据地址端口号。
     */
    get tilesetServePort() { return this._tilesetServePort.value; }

    /**
     * 获取数据地址端口号变化事件。
     * @returns 数据地址端口号变化事件。
     */
    get tilesetServePortChanged() { return this._tilesetServePort.changed; }

    /**
     * 设置数据地址端口号。
     * @param value - 要设置的数据地址端口号。
     */
    set tilesetServePort(value: string) { this._tilesetServePort.value = value; }

    /**
     * 获取端口号。
     * @returns 端口号。
     */
    async getport() {
        const port = await _getport(this.url as string, this.useEarthDSToken, this.earthdstoken);
        if (port) {
            this.tilesetServePort = port;
            return port;
        } else {
            throw new Error('服务地址port获取失败!');
        }
    }

    /**
     * 刷新图层。
     */
    async refresh() {
        if (!this.url || typeof this.url !== 'string') return;
        const u1 = this.url.match(/\/ts\/info\/(.*)/)?.[1]
        const time = +new Date();

        if (u1) {
            const ip = this.tileServiceIp;//http://localhost
            const port = await this.getport();//9014
            await this.getLayerConfig();
            this._es3DTileset.url = `${ip}:${port}/tiles/tileset.json` + `?t=${time}`;
        }
        console.log('3dtileset url:', this._es3DTileset.url);
    }

    /**
     * 获取当前服务的基础 URL。
     * @returns 当前服务的基础 URL。
     */
    get baseUrl() {
        if (this.url && typeof this.url === 'string') {
            const uri = this.url.match(/^(https?:\/\/[^\/]+)/)?.[1];
            if (!uri) {
                throw new Error('服务地址不合法,baseUrl is not found! 示例: http://localhost:9009/ts/info/ts01');
            }
            return uri;
        } else {
            throw new Error('服务地址不合法,baseUrl is not found! 示例: http://localhost:9009/ts/info/ts01');
        }
    }

    /**
     * 获取图层服务名称。
     * @returns 图层服务名称。
     */
    get tileServiceName() {
        if (this.url && typeof this.url === 'string') {
            const u1 = this.url.match(/\/ts\/info\/(.*)/)?.[1]
            if (!u1) {
                console.log(this.url);
                throw new Error('服务地址不合法,tileServiceName is not found! 示例: http://localhost:9009/ts/info/ms01,');
            }
            return u1
        } else {
            throw new Error('服务地址不合法,tileServiceName is not found! 示例: http://localhost:9009/ts/info/ms01');
        }
    }

    /**
     * 获取当前服务的 IP 地址。
     * @returns 当前服务的 IP 地址。
     */
    get tileServiceIp() {
        if (this.url && typeof this.url === 'string') {
            const ip = this.url.match(/^(https?:\/\/[^:]+)/)?.[1];
            if (!ip) {
                throw new Error('服务地址不合法,ip is not found! 示例: http://localhost:9009/ts/info/ts01');
            }
            return ip
        } else {
            throw new Error('服务地址不合法,ip is not found! 示例: http://localhost:9009/ts/info/ts01');
        }
    }

    /**
     * 构造函数。
     * @param id - 场景对象 ID。
     */
    constructor(id?: SceneObjectKey) {
        super(id);
        this.mainClass = this._es3DTileset;
        {
            const loadTileset = async () => {
                if (typeof this.url !== 'string') {
                    console.error('服务地址不合法, Init Error! 示例: http://localhost:9009/ts/info/ts01');
                    return;
                }
                if (!this.url) {
                    this._es3DTileset.url = ""
                } else {
                    this.refresh();
                }
            }
            // loadTileset();
            this.d(this.urlChanged.don(() => { loadTileset() }));
        }

        //属性同步
        {
            this.d(this.components.disposableAdd(this._es3DTileset));
            const tileset = this._es3DTileset;
            this.d(track([tileset, 'allowPicking'], [this, 'allowPicking']));
            this.d(track([tileset, 'collision'], [this, 'collision']));
            this.d(bind([tileset, 'flyToParam'], [this, 'flyToParam']));
            this.d(bind([tileset, 'flyToParam'], [this, 'flyToParam']));
            //此处url为服务地址，由服务端返回
            this.d(track([tileset, 'actorTag'], [this, 'actorTag']));
            this.d(track([tileset, 'materialMode'], [this, 'materialMode']));
            this.d(track([tileset, 'highlight'], [this, 'highlight']));
            this.d(track([tileset, 'maximumScreenSpaceError'], [this, 'maximumScreenSpaceError']));
            this.d(track([tileset, 'highlightID'], [this, 'highlightID']));
            this.d(track([tileset, 'highlightColor'], [this, 'highlightColor']));

            this.d(bind([tileset, 'offset'], [this, 'offset']));
            this.d(bind([tileset, 'rotation'], [this, 'rotation']));

            this.d(track([tileset, 'czmImageBasedLightingFactor'], [this, 'czmImageBasedLightingFactor']));
            this.d(track([tileset, 'czmLuminanceAtZenith'], [this, 'czmLuminanceAtZenith']));
            this.d(track([tileset, 'czmMaximumMemoryUsage'], [this, 'czmMaximumMemoryUsage']));
            this.d(track([tileset, 'czmClassificationType'], [this, 'czmClassificationType']));
            this.d(track([tileset, 'czmStyleJson'], [this, 'czmStyleJson']));
            this.d(track([tileset, 'czmBackFaceCulling'], [this, 'czmBackFaceCulling']));
            this.d(track([tileset, 'czmDebugShowBoundingVolume'], [this, 'czmDebugShowBoundingVolume']));
            this.d(track([tileset, 'czmDebugShowContentBoundingVolume'], [this, 'czmDebugShowContentBoundingVolume']));
            this.d(track([tileset, 'czmSkipLevelOfDetail'], [this, 'czmSkipLevelOfDetail']));

            this.d(track([tileset, 'colorBlendMode'], [this, 'colorBlendMode']));
            this.d(track([tileset, 'cacheBytes'], [this, 'cacheBytes']));

            this.d(track([tileset, 'clippingPlaneEnabled'], [this, 'clippingPlaneEnabled']));
            this.d(track([tileset, 'unionClippingRegions'], [this, 'unionClippingRegions']));
            this.d(track([tileset, 'clippingPlaneEdgeColor'], [this, 'clippingPlaneEdgeColor']));
            this.d(track([tileset, 'clippingPlaneEdgeWidth'], [this, 'clippingPlaneEdgeWidth']));
            this.d(track([tileset, 'materialParams'], [this, 'materialParams']));

            this.d(bind([tileset, 'supportEdit'], [this, 'supportEdit']));
            this.d(bind([tileset, 'clippingPlanesId'], [this, 'clippingPlanesId']));
            this.d(bind([tileset, 'clippingPlaneIds'], [this, 'clippingPlaneIds']));
            this.d(bind([tileset, 'flattenedPlaneId'], [this, 'flattenedPlaneId']));
            this.d(bind([tileset, 'flattenedPlaneEnabled'], [this, 'flattenedPlaneEnabled']));
            this.d(bind([tileset, 'clippingPlaneId'], [this, 'clippingPlaneId']));
            this.d(bind([tileset, 'excavateId'], [this, 'excavateId']));
        }
        //函数同步
        {
            const es3DTileset = this._es3DTileset;
            this.d(this.refreshTilesetEvent.don(() => { es3DTileset.refreshTileset(); }));
            this.d(this.highlightFeatureEvent.don((...arg) => { es3DTileset.highlightFeature(...arg); }));
            this.d(this.highlightFeatureAndFlyToEvent.don((...arg) => { es3DTileset.highlightFeatureAndFlyTo(...arg); }));
            this.d(this.setFeatureColorEvent.don((...arg) => { es3DTileset.setFeatureColor(...arg); }));
            this.d(this.setFeatureVisableEvent.don((...arg) => { es3DTileset.setFeatureVisable(...arg); }));
            this.d(this.resetFeatureStyleEvent.don((...arg) => { es3DTileset.resetFeatureStyle(...arg); }));
            this.d(this.setFeatureStyleEvent.don((...arg) => { es3DTileset.setFeatureStyle(...arg); }));
            this.d(this.flyInEvent.don((...arg) => { es3DTileset.flyIn(arg[0]); }));
            this.d(this.flyToEvent.don((...arg) => { es3DTileset.flyTo(arg[0]); }));
            this.d(this.calcFlyToParamEvent.don((...arg) => { es3DTileset.calcFlyToParam(...arg); }));
            this.d(this.calcFlyInParamEvent.don((...arg) => { es3DTileset.calcFlyInParam(...arg); }));
        }
        //事件同步
        {
            const es3DTileset = this._es3DTileset;
            this.d(es3DTileset.tilesetReady.don((tileset) => { this.tilesetReady.emit(tileset); }));

            this.d(es3DTileset.pickedEvent.don((pickedInfo) => {
                pickedInfo.assign({ sceneObject: this })
                this.pickedEvent.emit(pickedInfo);
            }));
        }
        //显隐控制
        {
            const updateShow = () => {
                this.es3DTileset.show = this.show;
            }
            updateShow();
            this.d(this.showChanged.don(() => { updateShow() }));
        }
    }
    /**
     * 获取属性列表。
     * @returns 属性列表。
     */
    override getESProperties() {
        const properties = { ...super.getESProperties() };
        return {
            ...properties,
            basic: [
                ...properties.basic,
                new JsonProperty([this, 'layerConfig'], '图层配置', [], undefined, true),
            ]
        }
    }
}
/**
 * ESRtsTileset2 接口，继承自 UniteChanged。
 */
export interface ESRtsTileset2 extends UniteChanged<ReturnType<typeof ESRtsTileset2.createDefaultProps>> { }